/*  Copyright 2008 Rene Weiskircher 
    This file is part of SUPA.

    SUPA is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    SUPA is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with SUPA.  If not, see <http://www.gnu.org/licenses/>.
 */
package supa;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
import java.util.Queue;

/**
 * Class representing a process that renders one frame of the animation
 * @author Rene Weiskircher (rene.weiskircher@gmail.com)
 *
 */
public class PovrayProcessor {
	/**
	 * Exception thrown if one of the rendering processes exits with something else than 0
	 * @author Rene Weiskircher
	 *
	 */
	public class ProcessFailedException extends Exception {
		/* not clear if we need something here */
	}
	/**
	 * Exception thrown if the method renderFrame is called and the processor is not idle
	 * @author Rene Weiskircher
	 *
	 */
	public class ProcessNotReadyException extends Exception {
		/* not clear if we need something here */
	}
	/**
	 * Class for storing a processing event where a frame is assigned
	 * to a processor
	 * 
	 *
	 */
	public class ProcessEvent {
		/**
		 * creates a new Process event with the given arguments
		 * @param _id ID of the processor
		 * @param nextFrame the frame being rendered
		 */
		public ProcessEvent(int _id, int nextFrame) {
			processor = _id;
			frame = nextFrame;
		}
		/**
		 * number of the processor
		 */
		public final int processor;
		/**
		 * number of the frame
		 */
		public final int frame;
	}
	/**
	 * The command for running povray
	 */
	public final static String povrayArgs = " %s +SF%d +EF%d";
	/**
	 * Path of ini-file
	 */
	private static File povrayIniFile;
	/**
	 * Path to povray command
	 */
	private static String _povrayCommand;
	/**
	 * The last process started
	 */
	private Process _proc;
	/**
	 * ID of this processor
	 */
	private final int _id;
	/**
	 * The status of this processor
	 */
	private String _status;
	/**
	 * the new frames assigned will be put into this queue
	 */
	private Queue<ProcessEvent> _queue;


	/**
	 * Set the povray executable and the ini-file of the animation for the processors
	 * @param executable the file corresponding to the povray executable
	 * @param iniFile the file containing the animation parameters
	 */
	public static void setFiles(File executable,File iniFile) {
		_povrayCommand = executable.getAbsolutePath() + povrayArgs;
		povrayIniFile = iniFile;
	}
	/**
	 * Creates a new PovrayProcessor with the given ID.
	 * @param id
	 * @param q the array of observers
	 */
	public PovrayProcessor(final int id, Queue<ProcessEvent> q) {
		_id = id;
		this._queue = q;
	}

	/**
	 * @return the the ID of the processor
	 */
	public int getId() {
		return _id;
	}
	/**
	 * @return the current status
	 */
	public String getStatus() {
		return _status;
	}
	/**
	 * @return true if the processor is currently idle
	 * @throws ProcessFailedException 
	 * @throws IOException 
	 */
	public boolean idle() throws ProcessFailedException, IOException {
		if(_proc == null)
			return true;
		int exitVal = 0;
		try {
			exitVal = _proc.exitValue();
			if(exitVal != 0) {
				InputStream err = _proc.getErrorStream();
				PrintStream errOut = System.err;
				int numBytes = err.available();
				byte contents[] = new byte[numBytes];
				err.read(contents);
				errOut.write(contents);
				errOut.flush();
				throw new ProcessFailedException();
			}
		}
		catch(IllegalThreadStateException il) {
			InputStream iStream = _proc.getInputStream();
			//throw the output of the process away
			iStream.skip(iStream.available());
			return false;
		}
		_proc = null;
		return true;
	}

	/**
	 * Starts a new process that renders the frame with the given number
	 * @param nextFrame number of the frame to be rendered
	 * @throws ProcessNotReadyException 
	 * @throws IOException 
	 */
	public void renderFrame(int nextFrame) throws ProcessNotReadyException, IOException {
		_status = Integer.toString(nextFrame);
		if(_proc != null) throw new ProcessNotReadyException();
		// we create a new process that renders the frame with the given number
		String completedCommand = String.format(_povrayCommand, povrayIniFile,nextFrame,nextFrame);
		ProcessBuilder procBuild = new ProcessBuilder(completedCommand.split(" "));
		_proc = procBuild.start();
		synchronized (_queue) {
			_queue.add(new ProcessEvent(_id,nextFrame));			
		}
	}

}
